//
// Created by Administrator on 2021/9/24.
//

#include "Shader.h"

Shader::Shader() {
    //读取文件
    std::string vsSource = FileUtil::readStrFromFile(VSHADER_FILE_PATH);
    std::string fsSource = FileUtil::readStrFromFile(FSHADER_FILE_PATH);

    initProgram(vsSource.c_str(), fsSource.c_str());
}

Shader::Shader(const char* vsFilePath, const char* fsFilePath) {
    //读取文件
    std::string vsSource = FileUtil::readStrFromFile(vsFilePath);
    std::string fsSource = FileUtil::readStrFromFile(fsFilePath);

    initProgram(vsSource.c_str(), fsSource.c_str());
}

void Shader::initAttributeArrays() {
    //顶点
    ShaderAttribSetter a_vertex;
    a_vertex.type = ShaderAttribType::VERTEX;
    a_vertex.index = 0;
    a_vertex.numPoints = 3;
    a_vertex.glType = 0;
    a_vertex.normalize = false;
    this->shaderAttribSetter.push_back(a_vertex);

    //颜色
    ShaderAttribSetter a_color;
    a_color.type = ShaderAttribType::COLOR;
    a_color.index = 1;
    a_color.numPoints = 4;
    a_color.glType = 0;
    a_color.normalize = false;
    this->shaderAttribSetter.push_back(a_color);
}

/**
 * 编译Shder源码
 * @param vertexShaderSource  vs
 * @param fragmentShaderSource fs
 */
void Shader::initProgram(const char* vertexShaderSource, const char* fragmentShaderSource) {
    //标识编译和链接是否成功
    int logLength = 512;
    int success;
    char infoLog[logLength];
    unsigned  int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if(!success){
        glGetShaderInfoLog(vertexShader, logLength, NULL, infoLog);
        log_error("vertexShader-compile failed.%s", infoLog);
        assert(false);
        return;
    }

    unsigned  int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if(!success){
        glGetShaderInfoLog(fragmentShader, logLength, NULL, infoLog);
        log_error("fragmentShader-compile failed.%s", infoLog);
        assert(false);
        return;
    }

    unsigned int program = glCreateProgram();
    glAttachShader(program, vertexShader);
    glAttachShader(program, fragmentShader);
    glLinkProgram(program);
    glGetProgramiv(program, GL_LINK_STATUS, &success);
    if(!success){
        glGetProgramInfoLog(program, logLength, NULL, infoLog);
        log_error("link-program failed.%s", infoLog);
        assert(false);
        return;
    }

    glDetachShader(program, vertexShader);
    glDetachShader(program, fragmentShader);
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    this->program = program;
}

void Shader::initAttribVBOArray(ShaderAttribType type, std::vector<float> attriArray) {
    float v[attriArray.size()];
    std::copy(attriArray.begin(), attriArray.end(), v);

    ShaderAttribSetter * psetter = nullptr;
    std::vector<ShaderAttribSetter>::iterator it;
    for (it = shaderAttribSetter.begin(); it != shaderAttribSetter.end(); it++){
        if(it->type == type){
            psetter = it.base();
            break;
        }
    }

    if(nullptr != psetter){
        unsigned int VAO;
        unsigned int VBO;
        glGenVertexArrays(1, &VAO);
        glBindVertexArray(VAO);
        glGenBuffers(1, &VBO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(v), v, GL_STATIC_DRAW);
        psetter->VBO = VBO;
        psetter->setVBOFlag = true;

        if(ShaderAttribType::VERTEX == type){
            drawCt = attriArray.size() / psetter->numPoints;
        }
        if(ShaderAttribType::INDICES == type){
           indicesFlag = true;
        }
    }

    log_info("Shader set attribute array,%d", psetter->VBO);
}

void Shader::setAttribPointer() {
    for( ShaderAttribSetter setter : shaderAttribSetter){
        if(setter.setVBOFlag) {
            glBindBuffer(GL_ARRAY_BUFFER, setter.VBO);
            glEnableVertexAttribArray(setter.index);
            glVertexAttribPointer(setter.index, setter.numPoints, switchGlType(setter.glType), setter.normalize, 0, (void *) 0);
        }
    }
}

void Shader::setUniform(const char *uniformVar, const Matrix4x4& m) const {
    //vector全部转到数组
    float mt[m.data.size()];
    memcpy(mt, &m.data[0], m.data.size() * sizeof(m.data[0]));
    glUniformMatrix4fv(glGetUniformLocation(program, uniformVar), 1, GL_FALSE, mt);
}

void Shader::draw(GLuint drawModel) {
    glUseProgram(program);
    setAttribPointer();
    if(indicesFlag) glDrawElements(drawModel, drawCt, 0, nullptr);//GLenum mode, GLsizei count, GLenum type, const void *indices TODO 这有问题
    glDrawArrays(drawModel, 0 , drawCt);
}

//0:GL_FLOAT 1:GL_UNSIGNED_INT 2:GL_UNSIGNED_BYTE TODO 这里后面考虑优化
unsigned int Shader::switchGlType(int type) {
    unsigned int glType = GL_FLOAT;
    switch (type) {
        case 0:
            glType = GL_FLOAT;
            break;
        case 1:
            glType = GL_UNSIGNED_INT;
            break;
        case 2:
            glType = GL_UNSIGNED_BYTE;
            break;
        default:
            glType = GL_FLOAT;
    }
    return glType;
}
